perm filename MVHL.PUB[HAL,HE] blob sn#133578 filedate 1974-12-04 generic text, type C, neo UTF8
COMMENT ⊗   VALID 00010 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00002 00002	.vhl: NEWSEC VERY HIGH LEVEL LANGUAGE CAPABILITIES
C00008 00003	.NEWSS MACRO OPERATIONS AS A α`HIGH LEVEL LANGUAGEα',MACRO-OPERATIONS
C00010 00004	.NEWSS MORE POWERFUL PRIMITIVES -- AN OVERVIEW,HIGH-LEVEL PRIMITIVES
C00016 00005	.chl:NEWSS CALLING HIGH LEVEL PRIMITIVES
C00023 00006	.wld:NEWSS WORLD MODELLING OVERVIEW
C00027 00007	.var: NEWSS INFORMATION ABOUT VARIABLES
C00036 00008	.obd: NEWSS OBJECT DESCRIPTION
C00046 00009	.NEWSSS ASSEMBLIES
C00049 00010	.NEWSS EXAMPLE: WATERPUMP ASSEMBLY PROGRAM,WATERPUMP ASSEMBLY
C00059 ENDMK
C⊗;
.vhl: NEWSEC VERY HIGH LEVEL LANGUAGE CAPABILITIES
.NEWSS INTRODUCTION 

To date, manipulator control languages have been very explicit,
with the user giving detailed specifications of what
motions are to be made,  what sensors are to be tested, etc. To some
extent this is also true of AL.  One can conceive of programming
complicated assemblies using only MOVE and OPERATE statements, 
condition monitors, and the like.
In practice, however,  such an approach has
many disadvantages for users,
who frequently don't care about
all the details needed to produce a program at the manipulator level.
For instance,  an assembly engineer who wants to put an engine
together might typically want to write something like:
.unfill
	:
	FIT enginehead ONTO engineblock
		WITH ALIGNMENT stud_x IN headhole_x,
			       stud_y IN headhole_y;
	INSERT bolt1 IN headhole1
		USING TOOL driver
		WITH TORQUE 10;
	INSERT bolt2 IN headhole2
		USING TOOL driver
		WITH TORQUE 10;
	INSERT drainplug IN sidehole;
	:

.refill
and allow the system to fill in the details, rather than coding up
all the motions herself.

This chapter gives an overview of those parts of AL that allow the
user to specify tasks at somewhat more convenient levels of
abstraction than provided by the manipulation control statements
alone.  Here, we are concerned with a "semi-automatic" programming
system that can make a number of the specific decisions required to
turn an "high level" task description into a running program and
which can ask for (and accept) help for those details that it cannot
determine for itself.

The range of decisions that the system may have to make is quite
broad, ranging from very local matters, such as the number of
trajectories which must be planned to ensure correct performance of all
cases of some motion request, to rather global decisions, such as
how an object should be grasped, how an object orientation
is to be determined, or what should be the relative order for
executing several related subtasks.

Some of these decisions are essentially domain-independent.  For
example, the system decides how many different arm trajectories it
must plan for a given MOVE statement by examining its model of the
locus of the destination frame (see {ssref var}), without much regard for the
"meaning" of the frame variable.  Other decisions may require a
significant degree of specialized knowledge about the task domain.
For instance, the INSERT primitive in our example would need to know
how a nutdriver is used to grasp a bolt, what effects (if any) the
shape of the bolt tip or hole chamfer has on choice of search
method, what constraints are imposed on workpiece positioning, and
much more. One very important constraint on the output program is
that it be consistent, in the sense that code generated to accomplish
one subtask should not be inconsistent with (and, indeed, should
facilitate) the accomplishment of other subtasks.  This necessity, in
turn, frequently generates further requirements for specialized
knowledge about the requirements and effects of the primitives being
provided.  

We have chosen small scale industrial automation as a good domain
for investigating the issues involved in incorporating 
such specialized knowledge into AL, and this discussion is
oriented accordingly.
However, many of the mechanisms underlying the various language features 
discussed here are fairly general; an expert system
for some other manipulatory domain could be organized along the same
lines and,  indeed, could use at least some of the same primitives.
The ease of such adaptation would depend, of course, on the closeness
of the domains and on the particular primitives involved.


.NEWSS MACRO OPERATIONS AS A α`HIGH LEVEL LANGUAGEα',MACRO-OPERATIONS

One obvious partial solution is to combine commonly
occuring code sequences into "macro operations",  and then allow the
user to specify tasks in terms of those operations.  AL includes
sophisticated macro, defined routine, and conditional compilation
facilities (see {secref ctc}) for this purpose.
Such library routines
have the advantage of being relatively easy for a person familiar
with AL to write, and are generally at least partially
self-documenting.  Typically, a user wishing to know what a given
library routine does can find out merely by looking at a listing of
the routine, which will (of course) be written in a clear, well
structured style with many comments describing the more obscure
passages.  Generally, such libraries are most useful where there is
essentially only one way to do a given subtask,  all actions required
to do each subtask can be performed at one place in the output
program, and different subtasks are essentially independent. When
these conditions are not fully met, more powerful techniques are
needed.

.NEWSS MORE POWERFUL PRIMITIVES -- AN OVERVIEW,HIGH-LEVEL PRIMITIVES

Many domains are sufficiently complicated that macro expansion, even
when used with conditional compilation, is too limited.
In assembly, there may be a number of different ways to do some
particular task; which way is "right" depends very largely on what
other subtasks must be done. Similarly, it is frequently possible to
perform part of one subtask (or, at least, to gather useful
information) in the course of doing another one.
Such considerations are in general very
difficult to express within the paradigm of macro expansion.

In our introductory
example, for instance, the system must decide how the engine block is
to be oriented to facilitate putting on the head.  Furthermore, the
block alignment must be sufficiently well determined so that the the
aligning studs can find their way into the holes. One way to do this
might be to push the engine block up against a simple aligning jig
consisting of a low wall.  Other methods might include vision or
simply grasping key features of the block and reading the hand
coordinates.  Once the head is on, the system must insert the bolts
and drainplug.  The system would like to avoid moving the engine
block around more than it has to, since each move requires time and
may introduce uncertainties.  This means that it should choose a
block position that allows the arm to reach the bolt and drain holes.
If an aligning jig is in use, care should be taken to keep the side
hole free if possible, and so forth. Furthermore, an alignment technique that
visually locates the engine block head holes is apt to yield more
useful information for the insertion tasks than would some cruder,
but less expensive, test which may work just as well for the purpose
of mating the head.

A full discussion of the mechanisms used by the system to
transform a high level program into one that can actually run is
beyond the scope of this paper.  Briefly, AL works by progressive
refinement of the user's program specifications, and uses process
instantiation and communication mechanisms to keep track of the
various subtasks and to ensure that all decisions are mutually
compatible.  Knowledge about assembly primitives is encoded into a
number of procedures inside the expander.  With each program
statement, the system associates a process instantiation of the
appropriate procedure (of course, the processes for low-level AL
statements are fairly trivial). These processes are then arranged
into a prerequisite graph based initially on the user's specification
of what must be done before what (see {sssref tsk}).  A number of
other "bureaucrat" processes are created to work out compromises,
invent new service tasks, decide relative ordering, watch out for
obvious inefficiencies (such as putting down a tool and then picking
it right back up again), and so on.  As the plan becomes more detailed, and
as decisions are made about the order in which subtasks are to be
performed, successive copies of the program graph are generated.
(Additional information is stored both in the data base and in 
the internal state of the various subtask processes.)
The final phase is to run down the (linearized) graph, asking each
subtask process to generate the appropriate output code.

.chl:NEWSS CALLING HIGH LEVEL PRIMITIVES

The syntax for high level primitives is keyword-oriented and
resembles that for MOVE and OPERATE statements in the sense
that there is a main clause naming the operation, with possibly
a number of subordinate clauses giving further specifications 
as to what is to be done.  For example,
.unfill
	INSERT screw1 INTO hole1    α{%4main clause%*α}
		USING TOOL nutdriver α{%4subordinate clause%*α}
		WITH TORQUE = 10 α{%4subordinate clause%*α}

.refill
For convenience and readability, a number of different forms
are acceptable.  For instance, the words "WITH" and "USING"
are interchangeable, and punctuation (like the "=", above) 
is frequently optional.  Some of the subordinate clauses
may themselves contain several elements, as in
.unfill
	FIT carburetor ONTO engine_assembly_1
		WITH ALIGNMENT 
			carburetor_hole1 OVER stud1,
			carburetor_hole2 OVER stud2;
.refill
In such cases, a comma is used to delimit successive elements.

Initially, only a fairly small set of high level primitives is
being implemented, although some (like INSERT) may be quite flexible.
Even a few primitives, however, turn out to be sufficient for
many interesting tasks, and provide quite a rich environment for
investigation of how the various parts of the system interact.

Probably the most elaborate primitive is INSERT, which is generally
responsible for insertion of shafts and shaft-like objects 
(including screws) into 
holes.  The main clause is 
.unfill
	INSERT <shaft-specification> INTO <hole-specification>
.refill
where the <shaft-specification> should be either an object
of type shaft or one end of an object of type shaft.  In the
former case, the system will assume that the "bottom end"
of the specified shaft should be inserted  into the named
hole. (See {ssref obd})  Similarly, the <hole specification> may be 
either the name of a hole or of a bore, in which case the 
top end will be assumed.  AL can learn a good part of 
exactly what it is being asked to do by looking at the 
object models.  For instance, if the shaft and bore are
both threaded and have the same diameter, then the system
will attempt to screw in the shaft properly.  Similarly, by
looking at the chamfer of the hole, the taper of the shaft,
and the region around the hole, the system can decide how 
much determination is required, what sort of search might
be applicable, etc.
Further specifications
may be included in subordinate clauses, such as the TOOL
and TORQUE clauses in our first example, or as in the
TWIST clause of
.unfill
	INSERT aligning_pin INTO guide_hole 
		WITH TWIST = 3
.refill
which says that the pin is to be given three turns counter-clockwise
as it is pushed into the guide hole.  Additional "advice" may also
be provided in the data base.  For instance, if there is a special
routine for grasping "type 1" screws with the nutdriver, there 
might be an assertion of the form:
.unfill
	FORM(GRASPING_METHOD, screwtype1, nutdriver, routineid)
.refill
(This example rather oversimplifies the actual mechanism
used to describe this sort of thing; a fuller description, however,
is beyond the scope of this paper.)

.unfill
.bull
Another fairly elaborate primitive is 
	FIT <object1> ONTO <object2>
.refill
where <object1> is usually a subpart of assembly <object2>. (See
{ssref obd} for more details about assemblies.)  If
this is not the case, then the attachment location must be specified
by a clause of the form
.unfill
	AT <transform>
.refill
The most common modifying clause for this primitive is an
alignment specification, such as
.unfill
	WITH ALIGNMENT 
		<hole> OVER <shaft>,
		<obj 1 feature> MEETS <obj 2 feature>,
		<shaft> INTO <hole>

.maybreak
.bull
Other primitives include:

	SLIP <collar> OVER <shaft>  α{includes nut over threaded shaftα}

	PLACE <object> ON <surface>
		IN POSITION <stable position>  α{optionalα}

	GRASP <object> AT <trans or frame>

	TIGHTEN <bolt or nut> 
		WITH TORQUE <number>  α{this clause is requiredα}

	EXTRACT <shaft>  α{the inverse of INSERTα}

.refill
.wld:NEWSS WORLD MODELLING OVERVIEW

The planning information required by the very high level primitives
is essentially a superset of that required for the basic
manipulation control statements; the same underlying mechanisms are
used, although sometimes in slightly different ways.  This includes
information about variable semantics, object shape and structure,
error estimates, and the purposes of programs, in addition to the
simple planning values and attachment structures used for low-level
trajectory planning.

The expander frequently needs to consider the effects of some hypothetical
action on a number of program steps.  Similarly, it is often necessary
to consider the effects of modifying some earlier decision or to
find a way to perform some preparatory action at an early point in
a program.  AL handles provides for this sort of consideration by
the use of a simple "multi-world" data base.  Essentially, all fluent information
(such as planning values of variables, assertions, etc) is associated
with a set of "world" states for which it is true.  With every program
statement, AL then associates an "input world", which contains the
planning model of the environment just before the statement gets executed,
and an "output world" which will reflect the expected effects of
the statement on the runtime world.  Normally, these two "worlds" 
can be the same when only low-level AL statements are involved, since
such statements don't usually need to generate forward or backward references
to other planning states.  

Although multiple worlds are primarily intended for use by the expander,
a user can make explicit references to different worlds by using
.unfill
	IN <worldname>
.refill
in ASSERT and DENY statements and in the various PLAN constructs.
The plan-time atoms IWORLD and OWORLD always contain
AL's internal names for the current input and output worlds, respectively.
For example,
.unfill
	ATOM w;
	s ←← 1;
	wα←←#(IWORLD);
	:
	PLAN IF (α#(s)=1) IN α#(w) THEN
		s ←← 2;
	Comment, now α#(s)=2;
.refill
Note:  IN acts syntactically like a very high priority boolean
binary operator, so that 
.unfill
	α#(a)=1 IN α#(w1) %7∨%* α#(b)=1 ∧ α#(a)=2 IN α#(w2) 
.bull
is equivalent to

	(α#(a)=1 IN α#(w1)) %7∨%* ((α#(b)=1 IN α#(IWORLD) ∧ (α#(a)=2 IN α#(w2))
.refill

.var: NEWSS INFORMATION ABOUT VARIABLES

The system must deal with a number of different sorts of information
about variables and variable values.  These include:
.begin narrow 8,8 

(1)%4 Metaphysical value.%* The metaphysical value of a variable is
that quantity which the variable is supposed to represent.
Traditionally, knowledge about the meaning of variables has been more
or less the exclusive province of the programmer.  For example, low level AL
constructs don't usually know or care what some user-declared frame
variable really represents, although the system does understand a few
predeclared variables (e.g., YELLOW, which gives the location of
the yellow arm).  On the other hand, a statement like "fit the pumphead
onto the pump assembly" requires AL to "know" what variables represent
object locations, mateing position, grasping positions, and so forth.

(2)%4 Runtime Value.%* This is the value that a given variable will
have at run time.  The compiler has a name for it, and must generate
code that references the corresponding memory location(s).

(3)%4 Locus information.%* Crudely put, the locus of a variable is
the set of possible runtime values for that variable. The term is
also used here to mean the compiler's estimate of the locus.  This
estimate may merely be the planning value, or it may include a 
region bounded by constraints.  These constraints may be expressed
explicitly, as mathematical relations involving degrees of freedom,
or implicitly, as semantic information like "the object is up
against the wall."

(4)%4 Determination information.%* The determination of a variable is
essentially a compile-time estimate of how accurately a runtime value
will reflect the corresponding metaphysical value.  As with the locus,
this information may be expressed in a number of ways.
.end


For example, suppose we we want to compile code to pick up an object
which we know will be sitting upright on the station.  In such a case,
the object will be free to rotate about the station z-axis and will
be free to move in the station x-axis and y-axis directions.  If we assume
that the station is 15 inches square, this might be translated by the
system into something like:
.unfill
	ASSERT FORM ( LOCUS,obj,
		EXPRESSION( FRAME( ROT(Z,theta),VECTOR(xdf,ydf,0)) ));

	ASSERT xdf %7≥%* 0*INCHES; ASSERT xdf≤15*INCHES;
	ASSERT ydf %7≥%* 0*INCHES; ASSERT ydf≤15*INCHES;
	ASSERT theta %7≥%* -π*RAD; ASSERT theta %7≤%* π*RAD;
.refill
where "obj" is a FRAME variable giving the location of the object, and
"xdf", "ydf", and "theta" represent the degrees of freedom.

Of course, there may be additional constraints on where the object is.
For instance, suppose that the object is round and is known to have been
shoved up against a low wall running diagonally across the station.  
This might give us a constraint like:
.unfill
	ASSERT xdf+ydf=15*INCHES;

.bull
so that the object locus is now given by

	FRAME(ROT(Z,theta),VECTOR(xdf,ydf,0))	{lc1: neweq}
	0≤xdf≤10
	0≤ydf≤10
	xdf+ydf=15
	-π*RAD≤theta≤π*RAD
.refill
If the object had a flat side known to be shoved up against the
wall, then we could also pin down theta to some fixed value, such as
.unfill
	theta ←← 0.75*π*RAD
.refill
Suppose that we now call a vision routine to locate the
object to within one centimeter and three degrees.  The vision
routine will store some value, say 
.unfill
	FRAME(ROT(Z,90),VECTOR(10,5,0))
.refill
into the value cell for obj.  We clearly cannot know in
advance that this will be that value returned, so the locus estimate
given by {eqref lc1} will remain unchanged.
On the other hand, the determination
of obj has been improved to the point where the object can be picked
up.  In other words, if we execute the statement
.unfill
	MOVE YELLOW TO obj*objgrasp
.refill 
then we know that the yellow arm will wind up sufficiently close
to the nominal grasping point for the object for the picking-up
operation to succeed.  
In planning a trajectory to do this, the system will use its
nominal value for obj, which (in the absence of any better advice)
will be chosen at the center of the locus,
.unfill
	FRAME(NILROT,VECTOR(7.5,7.5,0) )
.refill
and then modified at runtime in the usual way.

This trajectory modification may
cause problems if the runtime value of obj gets too far from the
nominal value. To avoid this, the expander will ask the trajectory
calculator to evaluate the suitability of its trajectory for extreme
points of the locus of obj.  If the modification seems to be too
great, then the expander will ask for several trajectories to be
planned and will generate conditional tests to select the correct
one. We are currently investigating ways to facilitate this
communication between the expander and the trajectory calculator. 
One very simple, though painstaking, method is to simulate moves to
a number of spots.  A better way would be for the trajectory
calculator to generate constraints telling what regions a trajectory
is good for, but it is a bit too early yet to tell how feasible this
will be.  Similarly, we are investigating ways for using runtime
errors to determine when splitting of a region may be needed.

.obd: NEWSS OBJECT DESCRIPTION

This section is intended to  provide an overview of the
sorts of information  about objects that AL uses and
of how this information is currently specified.  It is
not intended to be a complete list of %4all%* the assertions
currently used or as a user's manual for building object
descriptions.

Our primary interest so far has been to investigate ways to use
descriptive information about objects, rather than to provide an
extremely elegant input language for the descriptions. This has
led us to specify explicitly a number of things which are, in
principle, computable from a more general shape description. We
expect that the process of describing an object to AL, which is
currently almost completely manual, will eventually become very
largely automated, with most of the information being either directly
available or computed from the output of computer-aided-design
programs. 

Currently, objects are described by assertions about their
"interesting" properties. These assertions follow a number of
conventions, so that the various high level primitives can use the
information, although a user can, of course manipulate it explicitly.
Shape is treated simply as another object attribute, and a several
different shape descriptions may be present for a given object.

.newsss ONE-PIECE OBJECTS

Objects are represented as tree-like structures; typically, the
"root" node contains information about the object as a whole, with
"leaf" nodes telling about interesting features. By convention, we
use a frame variable for the object name. (This variable is then
assumed to give the object location).
.unfill
.bull 
For example,
.begin
.narrow 8,8
FRAME valvebody,bore1,bore2,bore3;
PLANNING TRANS upright,upside_down;
PLANE topsurface;

upright ←← NILTRANS;
upside_down ←← TRANS(ROT(Y,π*RAD),VECTOR(0,0,1.8));

ASSERT FORM(TYPE,valvebody,OBJECT); 
ASSERT FORM(GEOMED,valvebody,"valve.b3d"); 

ASSERT FORM(SUBPART,valvebody,bore1); 
ASSERT FORM(SUBPART,valvebody,bore2);
ASSERT FORM(SUBPART,valvebody,bore3);
ASSERT FORM(SURFACE,valvebody,topsurface );
.maybreak

ASSERT FORM(STABLE_POSITION,valvebody,α#(upright));
ASSERT FORM(STABLE_POSITION,valvebody,α#(upside_down));

topsurface <= PLANE(valvebody*VECTOR(0,0,1.8),Z WRT valvebody);

ATTACH bore1 TO valvebody AT TRANS(NILROT,VECTOR(-1,0,2)) RIGIDLY;
ATTACH bore2 TO valvebody AT TRANS(NILROT,VECTOR(1,0,2)) RIGIDLY;
ATTACH bore3 TO valvebody AT TRANS(NILROT,VECTOR(1,0,3)) RIGIDLY;
.end
.refill
declares that "valvebody" is an object whose GEOMED description
is given by file "valve.b3d".  There are three interesting "subparts",
called "bore1", "bore2", and "bore3" and located at FRAME(NILROT,VECTOR(-1,0,2)),
FRAME(NILROT,VECTOR(1,0,2)), and FRAME(NILROT,VECTOR(1,0,3)), respectively.
Also, there
is a planar surface called "topsurface" located at PLANE(1.8*Z,Z)
in body coordinates.  The valvebody can sit on the station in two "stable
positions", upright and upside_down.  Then, the assertion 
.unfill
	ASSERT FORM(valvebody,ON_SURFACE,station,α#(upside_down))
.refill
tells the system that the location of the valve body will be given by
an expression of the form:
.unfill
	TRANS(ROT(Z,theta),VECTOR(a,b,0))*α#(upside_down)*station

.bull
For degrees of freedom theta, a, and b.  This reduces to

	FRAME(ROT(Z,theta'),VECTOR(a',b',1.8));
.refill
where theta',a',& b' are another set of free scalar variables.

The subparts, "bore1", "bore2", and "bore3" are further described by
assertions of the form:

.unfill
.begin
.narrow 8,8
ASSERT FORM(TYPE,bore1,BORE);
ASSERT FORM(DIAMETER,bore1,0.9);
ASSERT FORM(THREAD,bore1,32);
ASSERT FORM(LENGTH,bore1,0.30);
ASSERT FORM(TOP_END,bore1,hole1);
ASSERT FORM(BOTTOM_END,bore1,OPEN); 

ASSERT FORM(TYPE,hole1,HOLE);  
ASSERT FORM(LIES_IN,hole1,topsurface); 
ASSERT FORM(CHAMFER_DEPTH,hole1,0);
ASSERT FORM(CHAMFER_WIDTH,hole1,0);
ASSERT FORM(LIP_SIZE,hole1,(3/16));

      α{et ceteraα}
.end
.refill
Here the system recognizes the word "BORE" as saying that bore1 is
a negative cylinder (such as might result from a drilling operation).
The attributes  DIAMETER, THREAD, and LENGTH are obvious.  TOP_END
and BOTTOM_END, however, may require a bit more explanation.
The "top end" of a bore is always a hole -- ie, an intersection
between the bore and the object surface.  If the bore completely pierces
the object, then the bottom end will be also be a hole.  Otherwise,
it may be "OPEN"  (which means that it opens into some uninteresting 
cavity inside the object, "CLOSED" (which means that it comes to
an abrupt, but otherwise uninteresting, end), or a named surface
(which usually only happens for relatively large holes). 
See {newfig Bores and Holes,full}.

Frequently, a user wishes to declare a number of instances of a single
prototype.  This may be done by making assertions of the form:

.unfill
	ASSERT FORM(TYPE,<object>,<prototype>)      .

.bull
For instance, 

	ASSERT FORM(TYPE,s1,screwtype1);
	ASSERT FORM(TYPE,s2,screwtype1);
.REFILL
would declare s1 and s2 to be instances of screwtype1, where screwtype1
might be specified by
.unfill
	ASSERT FORM(TYPE,screwtype1,SHAFT);
	ASSERT FORM(DIAMETER,screwtype1,0.62);
	ASSERT FORM(LENGTH,screwtype1,2.44);
	ASSERT FORM(THREAD,screwtype1,28);
	ASSERT FORM(TOP_END,screwtype1,headtype1); 
	ASSERT FORM(BOTTOM_END,screwtype1,tiptype1);

	ASSERT FORM(TYPE,headtype1,CYL_HEAD);
	ASSERT FORM(SLOT,headtype1,HEX 0.53);

	ASSERT FORM(TYPE,tiptype1,FLAT_END);
.refill
Note that shafts also are considered to be directed and to have two
ends.  By convention, all screws, bolts, or similar objects are
assumed to have their heads at the "top" end.  
See {newfig Shafts,full}.

.NEWSSS ASSEMBLIES

An "assembly" is an object whose various subparts are removable.


For instance,
.unfill
	ASSERT FORM(TYPE,waterpump,ASSEMBLY);
	ASSERT FORM(SUBPART,waterpump,gasket);
	ASSERT FORM(SUBPART,waterpump,head);
	ASSERT FORM(SUBPART,waterpump,pumpbase);
	 :
	ASSERT FORM(gasket,FITS,ONTO,waterpump,
			AT,TRANS(NILROT,VECTOR(0,0,3)));
	 :
.refill
Such objects provide a convenient framework for assembly tasks.  
Typically, one of the subparts is chosen as a "base part", which
is used as an anchor
to which the remaining parts are added.

In addition to the usual sorts of object attributes and the
locations of the various subparts, assemblies usually contain
a number of "semantic" assertions about how things go together.
Some of this information is inherent in the design.  For instance,
.unfill
	ASSERT FORM(DESIGNED_TORQUE,screw1,40)
.refill
Other information comes from the geometry of the parts, and (as 
indicated earlier) could theoretically be computed from the 
shape description but is of
enough interest to be worth representing directly, especially
in cases where the computation required is non-trivial.
For example,
.unfill
	 :
	ASSERT FORM(MATED,pumpbase.top_surf,gasket.bottom_surf)
	ASSERT FORM(ALIGNED,head.bore1,gasket.bore1,pumpbase.bore1)
	ASSERT FORM(RUNS_THRU,s1,gasket.bore1;
	ASSERT FORM(RUNS_THRU,s1,head.bore1);
	ASSERT FORM(SCREWED_INTO,s1,pumpbase.bore1);
	 :
.refill
.NEWSS EXAMPLE: WATERPUMP ASSEMBLY PROGRAM,WATERPUMP ASSEMBLY

This short example is intended to give some feel for what a very high
level program for a simple task looks like.

The task here is to mate the pump head and gasket with the
pump base using two aligning pins, then to secure the head with six
machine screws.  This task requires only a few basic operations, the
principle ones being FIT ... ONTO and INSERT, and is very similar to
one actually performed by WAVE at Stanford.  The WAVE program for
this task consists of about 450 lines of "machine language"-like
code, and was written over a period of several weeks by Bob Bolles and
Lou Paul. (Most of this time was spent on improving WAVE and
developing techniques; more recent tasks of similar complexity
have taken on the order of three to eight days)
The same program, rewritten in low-level AL, would
be somewhat shorter, although it would still require a fair amount
of detailed effort on the part of the programmer.  
.unfill
pump: BEGIN 

REQUIRE "PUMP.075" SOURCE_FILE; 

.COMT 4
      α{This file would contain many assertions describing the
	pump assembly and all its subparts.  Eventually,
	such descriptions will most likely be produced
	as part of the output of design automation programs.
	See the section on object descriptions for a sampling
	of the sorts of things one might see here.α}
.END


REQUIRE "STATN.04" SOURCE_FILE;

.COMT 4
      α{Reads in description of the work station. This would
	include location of tool racks, standard "jigs" that
	may be available, etc.α}
.END
.maybreak

ASSERT FORM(pumpbase,ON_SURFACE,conveyor_belt,α#(upright));

.COMT 4
      α{This assertion tells the strategist the initial 
	location & "stable position" (ie "upright") of the pumpbase.
	The value of "upright" is assumed to be set up in
	"PUMP.075".

	The dynamic frame "conveyor_belt" is assumed to have been
	defined in "STATN.04".  Actually, such moving devices 
	won't be handled by early versions of AL.  An alternative
	would be to arrange the pumpbases in an array to
	one side of the work station (perhaps using an "egg carton"
	arrangement to make it easier to pick one out).α}
.END
.maybreak

ASSERT FORM(pumphead,ON_SURFACE,side_table,α#(onside));

.COMT 4
      α{A number of other assertions might go here.α}
.END
	
.maybreak
.COMT 4
      α{Here, we will use TASK BEGINs and allow the system to
	decide on the relative order of the various subtasks.α}
.END

aligning: TASK BEGIN 

	INSERT pin1 INTO pumpbase.hole1;
	INSERT pin1 INTO pumpbase.hole2

     END aligning;

.COMT 12
      α{Note here that we are allowing the system to decide
	how it will locate the pumpbase and whether it
	will leave it on the conveyor belt throughout the
	assembly or place it in some temporary work area.
	Of course, we could have made these decisions explicitly.
	For instance, 
		
.END
.begin
.narrow 4,0
		PLACE pumpbase ON station
			IN POSITION upright
			WITH ALIGNMENT left_side AGAINST wall1,
				       back_end AGAINST wall2;
.end
.COMT 12
	could have been the first statement of the program.
.maybreak

	"wall1" & "wall2" are low walls described in
	"STATN.04" and form a corner which could be used as 
	a simple jig.  "left_side" & "back_end" here would be 
	defined in "PUMP.075"  as components in the "footprint"
	of the pumpbase.  See the section on object description
	for further details.α}
.END

.maybreak
FIT gasket ONTO pumpbase_assembly 
	WITH ALIGNMENT gasket.hole1 OVER pin1,
		       gasket.hole2 OVER pin2;

.COMT 12
      α{The system uses its object model for the
	pumpbase assembly to tell it how the gasket
	fits onto the pumpbase.α}
.END
.maybreak

FIT pumphead ONTO pumpbase_assembly
	WITH ALIGNMENT head.hole1 OVER pin1,
		       head.hole2 OVER pin2;
.maybreak

boltdown: TASK BEGIN 

s3op:	INSERT s3 INTO head.hole3
		WITH TOOL driver1,
		WITH TORQUE hand_tight;
	END;
.maybreak

s4op:	INSERT s4 INTO head.hole4
		WITH TOOL driver1,
		WITH TORQUE hand_tight;

.maybreak
s5op:	INSERT s5 INTO head.hole5
		WITH TOOL driver1,
		WITH TORQUE hand_tight;
.maybreak

s6op:	INSERT s6 INTO head.hole6
		WITH TOOL driver1,
		WITH TORQUE hand_tight;
.maybreak

p1_out:	INSERT pin1 INTO rack.hole1;
	PREREQUISITE OF p1_out IS s3op;
.COMT 12
	      α{Once we have s3 in h3, then we can remove
		pin1 without fear of letting the
		head slip out of alignment, since pin2
		will stay until there is a screw in
		hole 4.α}
.END
.maybreak

p2_out:	INSERT pin2 INTO rack.hole2;
	PREREQUISITE OF p2_out IS s4op;

.maybreak
s1op:	INSERT s1 INTO head.hole1
		WITH TOOL driver1,
		WITH TORQUE hand_tight;
	PREREQUISITE OF s1op IS p1_out;
.maybreak

s2op:	INSERT s2 INTO head.hole2
		WITH TOOL driver1,
		WITH TORQUE hand_tight;
	PREREQUISITE OF s1op IS p1_out;

     END boltdown;

.maybreak
torque_head: TASK BEGIN 

	PLAN FOREACH FORM(SUBPART,pump_assembly,BIND(s)) DO
		PLAN IF FORM(TYPE,α#(s),screwtype1)
		     ∧ FORM(α#(s),designed_torque,BIND(t))) THEN
			TIGHTEN α#(s) WITH TORQUE α#(t)
				WITH TOOL driver1;

     END torque_head;

.COMT 12
      α{This will tighten all subparts of the pump assembly
	which are of type "screwtype1" and have a specified design
	torque.   It might expand into something like:
.END

.begin
.narrow 8,0
	torque_head: TASK BEGIN 
		TIGHTEN s1 WITH TORQUE t1 WITH TOOL driver1;
		TIGHTEN s2 WITH TORQUE t2 WITH TOOL driver1;
		:
		TIGHTEN s6 WITH TORQUE t6 WITH TOOL driver1;
		END torque_head;
	α}
.end	

.maybreak
PLACE pump_assembly ON conveyor_belt IN POSITION upright;

.COMT 4
      α{This will cause the system to pick an orientation
	for the completed pump assembly & put it on the conveyor.
	The system can, of course, "remember" the position
	it picks.  If there were some further task to
	be done on the pump, the system will know where
	to find it.α}

.END
END pump;

.refill
.MAKEFIG(Pump Assembly Station,full)
.next page
.allpic